home *** CD-ROM | disk | FTP | other *** search
/ Aminet 35 / Aminet 35 (2000)(Schatztruhe)[!][Feb 2000].iso / Aminet / game / shoot / ADescentSrc.lha / descent / main / multibot.c < prev    next >
C/C++ Source or Header  |  1998-04-24  |  35KB  |  1,198 lines

  1. /*
  2. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  3. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  4. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  5. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  6. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  7. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  8. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  9. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  10. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
  11. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*
  14.  * $Source: /usr/CVS/descent/main/multibot.c,v $
  15.  * $Revision: 1.2 $
  16.  * $Author: tfrieden $
  17.  * $Date: 1998/04/24 14:30:04 $
  18.  * 
  19.  * Multiplayer robot code
  20.  * 
  21.  */
  22.  
  23. #pragma off (unreferenced)
  24. static char rcsid[] = "$Id: multibot.c,v 1.2 1998/04/24 14:30:04 tfrieden Exp $";
  25. #pragma on (unreferenced)
  26.  
  27. #ifndef SHAREWARE
  28.  
  29. #include <string.h>
  30. #include <stdlib.h>
  31.  
  32. #include "vecmat.h"
  33. #include "object.h"
  34. #include "multibot.h"
  35. #include "game.h"
  36. #include "modem.h"
  37. #include "network.h"
  38. #include "multi.h"
  39. #include "object.h"
  40. #include "laser.h"
  41. #include "error.h"
  42. #include "mono.h"
  43. #include "timer.h"
  44. #include "text.h"
  45. #include "ai.h"
  46. #include "fireball.h"
  47. #include "aistruct.h"
  48. #include "robot.h"
  49. #include "powerup.h"
  50. #include "scores.h"
  51. #include "gauges.h"
  52. #include "fuelcen.h"
  53. #include "morph.h"
  54. #include "digi.h"
  55. #include "sounds.h"
  56. #include "effects.h"
  57. #include "physics.h" 
  58.  
  59. #include "byteswap.h"
  60.  
  61. //
  62. // Code for controlling robots in multiplayer games
  63. //
  64.  
  65. #define STANDARD_EXPL_DELAY (F1_0/4)
  66. #define MIN_CONTROL_TIME    F1_0*2
  67. #define ROBOT_TIMEOUT       F1_0*3
  68.  
  69. #define MIN_TO_ADD  60
  70. #define MAX_TO_DELETE   67
  71.  
  72. #define db_printf(a) printf a
  73.  
  74. int robot_controlled[MAX_ROBOTS_CONTROLLED];
  75. int robot_agitation[MAX_ROBOTS_CONTROLLED];
  76. fix robot_controlled_time[MAX_ROBOTS_CONTROLLED];
  77. fix robot_last_send_time[MAX_ROBOTS_CONTROLLED];
  78. fix robot_last_message_time[MAX_ROBOTS_CONTROLLED];
  79. int robot_send_pending[MAX_ROBOTS_CONTROLLED];
  80. int robot_fired[MAX_ROBOTS_CONTROLLED];
  81. byte robot_fire_buf[MAX_ROBOTS_CONTROLLED][18+3];
  82.  
  83. #define MULTI_ROBOT_PRIORITY(objnum, pnum) (((objnum % 4) + pnum) % N_players)
  84.  
  85. //#define MULTI_ROBOT_PRIORITY(objnum, pnum) multi_robot_priority(objnum, pnum)
  86. //int multi_robot_priority(int objnum, int pnum)
  87. //{
  88. //  return( ((objnum % 4) + pnum) % N_players);
  89. //}
  90.  
  91. int
  92. multi_can_move_robot(int objnum, int agitation)
  93. {
  94.     // Determine whether or not I am allowed to move this robot.
  95.     int rval;
  96.  
  97.     // Claim robot if necessary.
  98.  
  99.     if (Player_exploded)
  100.         return 0;
  101.  
  102. #ifndef NDEBUG
  103.     if ((objnum < 0) || (objnum > Highest_object_index))
  104.     {   
  105.         Int3();
  106.         rval = 0;
  107.     }
  108.  
  109.     else if (Objects[objnum].type != OBJ_ROBOT)
  110.     {
  111.         Int3();
  112.         rval = 0;
  113.     }
  114. #endif
  115.  
  116.     else if ((Robot_info[Objects[objnum].id].boss_flag) && (Boss_dying == 1))
  117.         return 0;
  118.  
  119.     else if (Objects[objnum].ctype.ai_info.REMOTE_OWNER == Player_num) // Already my robot!
  120.     {
  121.         int slot_num = Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM;
  122.         Assert((slot_num > -1) && (slot_num < MAX_ROBOTS_CONTROLLED));
  123.  
  124.         if (robot_fired[slot_num]) {
  125. //          mprintf((0, "Preventing robot from firing again until firing complete!\n"));
  126.             rval = 0;
  127.         }
  128.         else {
  129.             robot_agitation[slot_num] = agitation;
  130.             robot_last_message_time[slot_num] = GameTime;
  131.             rval = 1;
  132.         }
  133.     }
  134.  
  135.     else if ((Objects[objnum].ctype.ai_info.REMOTE_OWNER != -1) || (agitation < MIN_TO_ADD))
  136.     {
  137.         if (agitation == ROBOT_FIRE_AGITATION) // Special case for firing at non-player
  138.         {
  139.             mprintf((0, "Shooting anyway.\n"));
  140.             rval = 1; // Try to fire at player even tho we're not in control!
  141.         }
  142.         else
  143.             rval = 0;
  144.     }
  145.     else
  146.         rval = multi_add_controlled_robot(objnum, agitation);
  147.  
  148.     return(rval);
  149. }
  150.  
  151. void
  152. multi_check_robot_timeout(void)
  153. {
  154.     static fix lastcheck = 0;
  155.     int i;
  156.  
  157.     if (GameTime > lastcheck + F1_0)
  158.     {
  159.         lastcheck = GameTime;
  160.         for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++) 
  161.         {
  162.             if ((robot_controlled[i] != -1) && (robot_last_send_time[i] + ROBOT_TIMEOUT < GameTime)) 
  163.             {
  164.                 if (Objects[robot_controlled[i]].ctype.ai_info.REMOTE_OWNER != Player_num)
  165.                 {       
  166.                     robot_controlled[i] = -1;
  167.                     Int3(); // Non-terminal but Rob is interesting, step over please...
  168.                     return;
  169.                 }
  170. //              Assert(Objects[robot_controlled[i]].ctype.ai_info.REMOTE_OWNER == Player_num);
  171.                 mprintf((0, "Robot %d (slot %d) removed, timed out, frame %d.\n", robot_controlled[i], i, GameTime));
  172.                 if (robot_send_pending[i])
  173.                     multi_send_robot_position(robot_controlled[i], 1);
  174.                 multi_send_release_robot(robot_controlled[i]);
  175. //              multi_delete_controlled_robot(robot_controlled[i]);
  176. //              robot_controlled[i] = -1;
  177.             }
  178.         }
  179.     }           
  180. }
  181.  
  182. void
  183. multi_strip_robots(int playernum)
  184. {
  185.     // Grab all robots away from a player 
  186.     // (player died or exited the game)
  187.  
  188.     int i;
  189.  
  190.     if (Game_mode & GM_MULTI_ROBOTS) {
  191.     
  192.         if (playernum == Player_num)
  193.             for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  194.                 multi_delete_controlled_robot(robot_controlled[i]);
  195.  
  196.         for (i = 1; i <= Highest_object_index; i++)
  197.             if ((Objects[i].type == OBJ_ROBOT) && (Objects[i].ctype.ai_info.REMOTE_OWNER == playernum)) {
  198.                 Assert((Objects[i].control_type == CT_AI) || (Objects[i].control_type == CT_NONE) || (Objects[i].control_type == CT_MORPH));
  199.                 Objects[i].ctype.ai_info.REMOTE_OWNER = -1;
  200.                 if (playernum == Player_num)
  201.                     Objects[i].ctype.ai_info.REMOTE_SLOT_NUM = 4;
  202.                 else
  203.                     Objects[i].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  204.             }
  205.     }
  206.     // Note -- only call this with playernum == Player_num if all other players
  207.     // already know that we are clearing house.  This does not send a release
  208.     // message for each controlled robot!!
  209.  
  210. }
  211.  
  212. void
  213. multi_dump_robots(void)
  214.  
  215. {
  216.     // Dump robot control info for debug purposes
  217.  
  218.     int i;
  219.  
  220.     if (!(Game_mode & GM_MULTI_ROBOTS))
  221.         return;
  222.  
  223.     mprintf((0, "\n"));
  224.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  225.     {
  226.         mprintf((0, "Robot %d) objnum=%d, anger=%d, last_message=%d, last move=%d, gametime=%d\n", i, robot_controlled[i], robot_agitation[i], robot_last_message_time[i], robot_last_send_time[i], GameTime));
  227.     }
  228. }
  229.  
  230. int
  231. multi_add_controlled_robot(int objnum, int agitation)
  232. {
  233.     int i;
  234.     int lowest_agitation = 0x7fffffff; // MAX POSITIVE INT
  235.     int lowest_agitated_bot = -1;
  236.     int first_free_robot = -1;
  237.  
  238.     // Try to add a new robot to the controlled list, return 1 if added, 0 if not.
  239.  
  240.     if (Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM > 0)
  241.     {
  242.         mprintf((0, "Robot %d hands-off = %d.\n", objnum, Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM));
  243.         Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM -= 1;
  244.         return 0;
  245.     }
  246.  
  247.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  248.     {
  249.         if ((robot_controlled[i] == -1) || (Objects[robot_controlled[i]].type != OBJ_ROBOT)) {
  250.             first_free_robot = i;
  251.             break;
  252.         }
  253.  
  254.         if (robot_last_message_time[i] + ROBOT_TIMEOUT < GameTime) {
  255.             mprintf((0, "Robot %d replaced (timeout).\n", robot_controlled[i]));
  256.             if (robot_send_pending[i])
  257.                 multi_send_robot_position(robot_controlled[i], 1);
  258.             multi_send_release_robot(robot_controlled[i]);
  259.             first_free_robot = i;
  260.             break;
  261.         }
  262.  
  263.         if ((robot_controlled[i] != -1) && (robot_agitation[i] < lowest_agitation) && (robot_controlled_time[i] + MIN_CONTROL_TIME < GameTime))
  264.         {
  265.             lowest_agitation = robot_agitation[i];
  266.             lowest_agitated_bot = i;
  267.         }
  268.     }
  269.  
  270.     if (first_free_robot != -1)  // Room left for new robots
  271.         i = first_free_robot;
  272.  
  273.     else if ((agitation > lowest_agitation) && (lowest_agitation <= MAX_TO_DELETE)) // Replace some old robot with a more agitated one
  274.     {
  275.         if (robot_send_pending[lowest_agitated_bot])
  276.             multi_send_robot_position(robot_controlled[lowest_agitated_bot], 1);
  277.         multi_send_release_robot(robot_controlled[lowest_agitated_bot]);
  278.  
  279.         mprintf((0, "Replaced robot %d (agitation %d) with robot %d (agitation %d).\n", robot_controlled[lowest_agitated_bot], lowest_agitation, objnum, agitation));       
  280.  
  281.         i = lowest_agitated_bot;
  282.     }
  283.     else {
  284. //      mprintf((0, "Cannot add robot %d agitation %d (min agitation %d)\n", objnum, agitation, lowest_agitation));
  285.         return(0); // Sorry, can't squeeze him in!
  286.     }
  287.  
  288.     mprintf((0, "Taking control of robot %d, agitation %d.\n", objnum, agitation));
  289.  
  290.     multi_send_claim_robot(objnum);
  291.     robot_controlled[i] = objnum;
  292.     robot_agitation[i] = agitation;
  293.     Objects[objnum].ctype.ai_info.REMOTE_OWNER = Player_num;
  294.     Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM = i;
  295.     robot_controlled_time[i] = GameTime;
  296.     robot_last_send_time[i] = robot_last_message_time[i] = GameTime;
  297.     return(1);
  298. }   
  299.  
  300. void
  301. multi_delete_controlled_robot(int objnum)
  302. {
  303.     int i;
  304.  
  305.     // Delete robot object number objnum from list of controlled robots because it is dead
  306.  
  307.     if ( (objnum<0) || (objnum>Highest_object_index))   {
  308. //      mprintf((0, "Trying to releasing control of bogus robot %d\n", objnum));
  309.         return;
  310.     }
  311.  
  312.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  313.         if (robot_controlled[i] == objnum)
  314.             break;
  315.  
  316.     if (i == MAX_ROBOTS_CONTROLLED)
  317.         return;
  318.  
  319. //  mprintf((0, "Releasing control of robot %d\n", objnum));
  320.  
  321.     Assert(Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM == i);
  322.  
  323.     Objects[objnum].ctype.ai_info.REMOTE_OWNER = -1;
  324.     Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  325.     robot_controlled[i] = -1;
  326.     robot_send_pending[i] = 0;
  327.     robot_fired[i] = 0;
  328. }
  329.  
  330. void
  331. multi_send_claim_robot(int objnum)
  332. {
  333.     if ((objnum < 0) || (objnum > Highest_object_index))
  334.     {
  335.         Int3(); // See rob
  336.         return;
  337.     }
  338.  
  339.     if (Objects[objnum].type != OBJ_ROBOT)
  340.     {
  341.         Int3(); // See rob
  342.         return;
  343.     }
  344.  
  345.     // The AI tells us we should take control of this robot. 
  346.  
  347.     multibuf[0] = (char)MULTI_ROBOT_CLAIM;
  348.     multibuf[1] = Player_num;
  349.     *(short *)(multibuf+2) = swapshort((short)objnum_local_to_remote(objnum, (byte *)&multibuf[4]));
  350.  
  351.     multi_send_data(multibuf, 5, 1);
  352. }
  353.  
  354. void
  355. multi_send_release_robot(int objnum)
  356. {
  357.     if ((objnum < 0) || (objnum > Highest_object_index))
  358.     {
  359.         Int3(); // See rob
  360.         return;
  361.     }
  362.  
  363.     if (Objects[objnum].type != OBJ_ROBOT)
  364.     {
  365.         Int3(); // See rob
  366.         return;
  367.     }
  368.  
  369.     multi_delete_controlled_robot(objnum);
  370.  
  371.     multibuf[0] = (char)MULTI_ROBOT_RELEASE;
  372.     multibuf[1] = Player_num;
  373.     *(short *)(multibuf+2) = swapshort((short)objnum_local_to_remote(objnum, (byte *)&multibuf[4]));
  374.  
  375.     multi_send_data(multibuf, 5, 1);
  376. }
  377.  
  378. #define MIN_ROBOT_COM_GAP F1_0/12
  379.  
  380. int
  381. multi_send_robot_frame(int sent)
  382. {
  383.     static int last_sent = 0;
  384.  
  385.     int i;
  386.     int rval = 0;
  387.  
  388.     for (i = 0; i < MAX_ROBOTS_CONTROLLED; i++)
  389.     {
  390.         int sending = (last_sent+1+i)%MAX_ROBOTS_CONTROLLED;
  391.         if ( (robot_controlled[sending] != -1) && ((robot_send_pending[sending] > sent) || (robot_fired[sending] > sent)) )
  392.         {
  393.             if (robot_send_pending[sending])
  394.             {
  395.                 robot_send_pending[sending] = 0;    
  396.                 multi_send_robot_position_sub(robot_controlled[sending]);
  397.             }
  398.  
  399.             if (robot_fired[sending])
  400.             {
  401.                 robot_fired[sending] = 0;
  402.                 multi_send_data(robot_fire_buf[sending], 18, 0);
  403.             }
  404.  
  405.             if (!(Game_mode & GM_NETWORK))
  406.                 sent += 1;
  407.  
  408.             last_sent = sending;
  409.             rval++;
  410.         }
  411.     }
  412.     Assert((last_sent >= 0) && (last_sent <= MAX_ROBOTS_CONTROLLED));
  413.     return(rval);
  414. }
  415.  
  416. void
  417. multi_send_robot_position_sub(int objnum)
  418. {
  419.     int loc = 0;
  420.  
  421. //  mprintf((0, "SENDPOS object %d, Gametime %d.\n", objnum, GameTime));
  422.  
  423.     multibuf[loc] = MULTI_ROBOT_POSITION;               loc += 1;
  424.     multibuf[loc] = Player_num;                             loc += 1;
  425.     *(short *)(multibuf+loc) = swapshort((short)objnum_local_to_remote(objnum, (byte *)&multibuf[loc+2]));
  426.                                                                         loc += 3;
  427.     create_shortpos((shortpos *)(multibuf+loc), Objects+objnum);
  428.  
  429.     bcopy(multibuf+loc+10, multibuf+loc+9 ,14);     // compensate for aligned struct
  430.                                                                         loc += sizeof(shortpos);
  431.  
  432.     multi_send_data(multibuf, loc-1, 0);
  433. }
  434.  
  435. void
  436. multi_send_robot_position(int objnum, int force)
  437. {
  438.     // Send robot position to other player(s).  Includes a byte
  439.     // value describing whether or not they fired a weapon
  440.  
  441.     if (!(Game_mode & GM_MULTI))
  442.         return;
  443.  
  444.     if ((objnum < 0) || (objnum > Highest_object_index))
  445.     {
  446.         Int3(); // See rob
  447.         return;
  448.     }
  449.     if (Objects[objnum].type != OBJ_ROBOT)
  450.     {
  451.         Int3(); // See rob
  452.         return;
  453.     }
  454.  
  455.     if (Objects[objnum].ctype.ai_info.REMOTE_OWNER != Player_num)
  456.         return;
  457.  
  458. //  Objects[objnum].phys_info.drag = Robot_info[Objects[objnum].id].drag; // Set drag to normal
  459.  
  460.     robot_last_send_time[Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM] = GameTime;
  461.  
  462.     robot_send_pending[Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM] = 1+force;
  463.  
  464.     if (force & (Game_mode & GM_NETWORK))
  465.         PacketUrgent = 1;
  466.  
  467.     return;
  468. }
  469.  
  470. void
  471. multi_send_robot_fire(int objnum, int gun_num, vms_vector *fire)
  472. {
  473.     // Send robot fire event
  474.     vms_vector swapped_vec;
  475.     int loc = 0;
  476.  
  477.     swapped_vec.x = (fix)swapint((int)fire->x);
  478.     swapped_vec.y = (fix)swapint((int)fire->y);
  479.     swapped_vec.z = (fix)swapint((int)fire->z);
  480.     multibuf[loc] = MULTI_ROBOT_FIRE;                       loc += 1;
  481.     multibuf[loc] = (char)Player_num;                       loc += 1;
  482.     *(short *)(multibuf+loc) = swapshort((short)objnum_local_to_remote(objnum, (byte *)&multibuf[loc+2]));
  483.                                                             loc += 3;
  484.     multibuf[loc] = gun_num;                                loc += 1;
  485.     *(vms_vector *)(multibuf+loc) = swapped_vec;            loc += sizeof(vms_vector); // 12
  486. //  *(vms_vector *)(multibuf+loc) = *fire;                  loc += sizeof(vms_vector); // 12
  487.     //                                                              --------------------------
  488.     //                                                                  Total = 18
  489.     if (Objects[objnum].ctype.ai_info.REMOTE_OWNER == Player_num)
  490.     {
  491.         int slot = Objects[objnum].ctype.ai_info.REMOTE_SLOT_NUM;
  492.         Assert((slot >= 0) && (slot < MAX_ROBOTS_CONTROLLED));
  493.         if (robot_fired[slot] != 0)
  494.             Int3(); // ROB!
  495.         memcpy(robot_fire_buf[slot], multibuf, loc);
  496.         robot_fired[slot] = 1;
  497. //      if (Game_mode & GM_NETWORK)
  498. //          PacketUrgent = 1;
  499.     }
  500.     else
  501.         multi_send_data(multibuf, loc, 0); // Not our robot, send ASAP
  502. }
  503.  
  504. void
  505. multi_send_robot_explode(int objnum, int killer)
  506. {
  507.     // Send robot explosion event to the other players
  508.  
  509.     int loc = 0;
  510.  
  511.     multibuf[loc] = MULTI_ROBOT_EXPLODE;                    loc += 1;
  512.     multibuf[loc] = (char)Player_num;                               loc += 1;
  513.     *(short *)(multibuf+loc) = swapshort((short)objnum_local_to_remote(killer, (byte *)&multibuf[loc+2]));
  514.                                                                         loc += 3;
  515.     *(short *)(multibuf+loc) = swapshort((short)objnum_local_to_remote(objnum, (byte *)&multibuf[loc+2]));
  516.                                                                         loc += 3;
  517.     multi_send_data(multibuf, loc, 1);
  518.  
  519.     multi_delete_controlled_robot(objnum);
  520. }
  521.  
  522. void
  523. multi_send_create_robot(int station, int objnum, int type)
  524. {
  525.     // Send create robot information
  526.  
  527.     int loc = 0;
  528.  
  529.     multibuf[loc] = MULTI_CREATE_ROBOT;                     loc += 1;
  530.     multibuf[loc] = Player_num;                             loc += 1;
  531.     multibuf[loc] = (byte)station;                          loc += 1;
  532.     *(short *)(multibuf+loc) = swapshort((short)objnum);    loc += 2;
  533.     multibuf[loc] = type;                                       loc += 1;
  534.  
  535.     map_objnum_local_to_local((short)objnum);
  536.  
  537.     multi_send_data(multibuf, loc, 1);
  538. }
  539.  
  540. void
  541. multi_send_boss_actions(int bossobjnum, int action, int secondary, int objnum)
  542. {
  543.     // Send special boss behavior information
  544.  
  545.     int loc = 0;
  546.     
  547.     multibuf[loc] = MULTI_BOSS_ACTIONS;                     loc += 1;
  548.     multibuf[loc] = Player_num;                             loc += 1; // Which player is controlling the boss
  549.     *(short *)(multibuf+loc) = swapshort(bossobjnum);       loc += 2; // We won't network map this objnum since its the boss
  550.     multibuf[loc] = (byte)action;                           loc += 1; // What is the boss doing?
  551.     multibuf[loc] = (byte)secondary;                        loc += 1; // More info for what he is doing
  552.     *(short *)(multibuf+loc) = swapshort(objnum);           loc += 2; // Objnum of object created by gate-in action
  553.     if (action == 3) {
  554.         *(short *)(multibuf+loc) = swapshort(Objects[objnum].segnum); loc += 2; // Segment number object created in (for gate only)
  555.     }
  556.     else
  557.                                                                         loc += 2; // Dummy
  558.  
  559.     if (action == 1) { // Teleport releases robot
  560.         // Boss is up for grabs after teleporting
  561.         mprintf((0, "Boss teleporting, removed from my list.\n"));
  562.         Assert((Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM >= 0) && (Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM < MAX_ROBOTS_CONTROLLED));
  563.         multi_delete_controlled_robot(bossobjnum);
  564. //      robot_controlled[Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM] = -1;
  565. //      Objects[bossobjnum].ctype.ai_info.REMOTE_OWNER = -1;
  566.         Objects[bossobjnum].ctype.ai_info.REMOTE_SLOT_NUM = 5; // Hands-off period!
  567.     }
  568.     if (action == 3) {
  569.         mprintf((0, "LOCAL:Boss gating in robot id %d, objnum %d (%d).\n", secondary, objnum, objnum));
  570.     }
  571.     multi_send_data(multibuf, loc, 1);
  572. }
  573.             
  574. #define MAX_ROBOT_POWERUPS 4
  575.  
  576. void
  577. multi_send_create_robot_powerups(object *del_obj)
  578. {
  579.     // Send create robot information
  580.  
  581.     int loc = 0;
  582.     int i;
  583.     vms_vector swapped_vec;
  584.  
  585.     swapped_vec.x = (fix)swapint((int)del_obj->pos.x);
  586.     swapped_vec.y = (fix)swapint((int)del_obj->pos.y);
  587.     swapped_vec.z = (fix)swapint((int)del_obj->pos.z);
  588.  
  589.     multibuf[loc] = MULTI_CREATE_ROBOT_POWERUPS;            loc += 1;
  590.     multibuf[loc] = Player_num;                             loc += 1;
  591.     multibuf[loc] = del_obj->contains_count;                loc += 1;
  592.     multibuf[loc] = del_obj->contains_type;                 loc += 1;
  593.     multibuf[loc] = del_obj->contains_id;                   loc += 1;
  594.     *(short *)(multibuf+loc) = swapshort((short)del_obj->segnum);           loc += 2;
  595.     *(vms_vector *)(multibuf+loc) = swapped_vec;        loc += 12;
  596. //  *(vms_vector *)(multibuf+loc) = del_obj->pos;       loc += 12;
  597.  
  598.     memset(multibuf+loc, -1, MAX_ROBOT_POWERUPS*sizeof(short));
  599.  
  600.     if ((Net_create_loc > MAX_ROBOT_POWERUPS) || (Net_create_loc < 1))
  601.     {
  602.         Int3(); // See Rob
  603.     }
  604.     for (i = 0; i < Net_create_loc; i++)
  605.     {
  606.         *(short *)(multibuf+loc) = swapshort((short)Net_create_objnums[i]);
  607.         loc += 2;
  608.         map_objnum_local_to_local(Net_create_objnums[i]);
  609.     }
  610.  
  611.     Net_create_loc = 0;
  612.  
  613.     multi_send_data(multibuf, 27, 1);
  614. }
  615.  
  616. void
  617. multi_do_claim_robot(char *buf)
  618. {
  619.     short botnum;
  620.     short remote_botnum;
  621.     char pnum;
  622.  
  623.     pnum = buf[1];
  624.  
  625.     remote_botnum = swapshort(*(short *)(buf+2));
  626.     botnum = objnum_remote_to_local(remote_botnum, (byte)buf[4]);
  627.  
  628. //    db_printf(("Player %d claiming robot %d! Grovel before your master!\n", pnum, botnum));
  629.  
  630.     if ((botnum > Highest_object_index) || (botnum < 0)) {
  631.         db_printf(( "Ignoring claim message for object I don't have.\n"));
  632.   //      Int3(); // See rob
  633.         return;
  634.     }
  635.  
  636.     if (Objects[botnum].type != OBJ_ROBOT) {
  637.         mprintf((1, "Got MYBOT message for non-Robot!\n"));
  638.         return;
  639.     }
  640.     
  641.     if (Objects[botnum].ctype.ai_info.REMOTE_OWNER != -1)
  642.     {
  643.         mprintf((0, "Got a MYBOT message bot %d (%d) currently controlled by player %d.\n", botnum, remote_botnum, Objects[botnum].ctype.ai_info.REMOTE_OWNER));
  644.         if (MULTI_ROBOT_PRIORITY(remote_botnum, pnum) <= MULTI_ROBOT_PRIORITY(remote_botnum, Objects[botnum].ctype.ai_info.REMOTE_OWNER))
  645.             return;
  646.     }
  647.     
  648.     // Perform the requested change
  649.  
  650.     mprintf((0, "Player %d taking control of robot %d (%d).\n", pnum, botnum, remote_botnum));
  651.  
  652.     if (Objects[botnum].ctype.ai_info.REMOTE_OWNER == Player_num)
  653.     {
  654.         multi_delete_controlled_robot(botnum);
  655.     }
  656.  
  657.     Objects[botnum].ctype.ai_info.REMOTE_OWNER = pnum;
  658.     Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  659. }
  660.  
  661. void
  662. multi_do_release_robot(char *buf)
  663. {
  664.     short botnum;
  665.     char pnum;
  666.  
  667.     pnum = buf[1];
  668.  
  669.     botnum = (objnum_remote_to_local(swapshort(*(short *)(buf+2)), (byte)buf[4]));
  670.  
  671.     if ((botnum < 0) || (botnum > Highest_object_index)) {
  672.         mprintf((1, "Ignoring release message for object I don't have.\n"));
  673. //      Int3(); // See rob
  674.         return;
  675.     }
  676.  
  677.     if (Objects[botnum].type != OBJ_ROBOT) {
  678.         mprintf((1, "Got RELEASE message for non-Robot!\n"));
  679.         return;
  680.     }
  681.     
  682.     if (Objects[botnum].ctype.ai_info.REMOTE_OWNER != pnum)
  683.     {
  684.         mprintf((0, "Got a RELEASE message for bot %d not controlled by player %d.\n", botnum, pnum));
  685.         return;
  686.     }
  687.     
  688.     // Perform the requested change
  689.  
  690.     mprintf((0, "Player %d releasing control of robot %d (%d).\n", pnum, botnum, *(short *)(buf+2)));
  691.  
  692.     Objects[botnum].ctype.ai_info.REMOTE_OWNER = -1;
  693.     Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  694. }
  695.  
  696. void
  697. multi_do_robot_position(char *buf)
  698. {
  699.     // Process robot movement sent by another player
  700.  
  701.     short botnum;
  702.     char pnum;
  703.     int loc = 1;
  704.  
  705.     pnum = buf[loc];                                        loc += 1;
  706.  
  707.     botnum = objnum_remote_to_local(swapshort(*(short *)(buf+loc)), (byte)buf[loc+2]);
  708.                                                                 loc += 3;
  709.  
  710.     if ((botnum < 0) || (botnum > Highest_object_index)) {
  711.         db_printf(( "Got robot position for object %d which I don't have.\n", botnum));
  712. //      Int3(); // See rob
  713.         return;
  714.     }
  715.  
  716.     if ((Objects[botnum].type != OBJ_ROBOT) || (Objects[botnum].flags & OF_EXPLODING)) {
  717.         db_printf(( "Ignoring position packet for non-robot or exploding robot.\n"));
  718.         return;
  719.     }
  720.         
  721.     if (Objects[botnum].ctype.ai_info.REMOTE_OWNER != pnum)
  722.     {   
  723.         if (Objects[botnum].ctype.ai_info.REMOTE_OWNER == -1)
  724.         {
  725.             // Robot claim packet must have gotten lost, let this player claim it.
  726.             if (Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM > 3) {
  727.                 db_printf(("Player %d taking control of robot %d WITHOUT claim message.\n", pnum, botnum));
  728.                 Objects[botnum].ctype.ai_info.REMOTE_OWNER = pnum;
  729.                 Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM = 0;
  730.             }
  731.             else
  732.                 Objects[botnum].ctype.ai_info.REMOTE_SLOT_NUM++;
  733.         }
  734.         else
  735.         {
  736.             db_printf(( "Ignoring position for robot %d not controlled by player %d.\n", botnum, pnum));
  737.             return;
  738.         }
  739.     }
  740.  
  741.     set_thrust_from_velocity(&Objects[botnum]); // Try to smooth out movement
  742. //  Objects[botnum].phys_info.drag = Robot_info[Objects[botnum].id].drag >> 4; // Set drag to low
  743.  
  744.     bcopy(buf+loc+9, buf+loc+10, 14);           // compensate for aligned struct
  745.     extract_shortpos(&Objects[botnum], (shortpos *)(buf+loc)); 
  746. }
  747.  
  748. void
  749. multi_do_robot_fire(char *buf)
  750. {
  751.     // Send robot fire event
  752.     int loc = 1;
  753.     int botnum, pnum, gun_num;
  754.     vms_vector fire, gun_point;
  755.     robot_info *robptr;
  756.  
  757.     pnum = buf[loc];                                                loc += 1;
  758.     botnum = objnum_remote_to_local(swapshort(*(short *)(buf+loc)), (byte)buf[loc+2]);
  759.                                                                         loc += 3;
  760.     gun_num = (byte)buf[loc];                                           loc += 1;
  761.     fire = *(vms_vector *)(buf+loc);                            
  762.     fire.x = (fix)swapint((int)fire.x);
  763.     fire.y = (fix)swapint((int)fire.y);
  764.     fire.z = (fix)swapint((int)fire.z);
  765.  
  766.     if ((botnum < 0) || (botnum > Highest_object_index) || (Objects[botnum].type != OBJ_ROBOT) || (Objects[botnum].flags & OF_EXPLODING))
  767.     {
  768. //      mprintf((1, "Ignored robot fire from deleted robot.\n"));
  769. //      Int3(); // See Rob, probably not serious tho
  770.         return;
  771.     }
  772.     
  773.     // Do the firing
  774.     
  775.     if (gun_num == -1)
  776.     {
  777.         // Drop proximity bombs
  778.         vm_vec_add(&gun_point, &Objects[botnum].pos, &fire);
  779.     }
  780.     else 
  781.     {
  782.         calc_gun_point(&gun_point, &Objects[botnum], gun_num);
  783.     }
  784.     robptr = &Robot_info[Objects[botnum].id];
  785.     
  786.     if (gun_num == -1) 
  787.         Laser_create_new_easy( &fire, &gun_point, botnum, PROXIMITY_ID, 1);
  788.     else
  789.         Laser_create_new_easy( &fire, &gun_point, botnum, robptr->weapon_type, 1);
  790. }
  791.  
  792. int
  793. multi_explode_robot_sub(int botnum, int killer)
  794. {
  795.     object *robot;
  796.  
  797.     killer = killer;
  798.  
  799.     if ((botnum < 0) || (botnum > Highest_object_index)) { // Objnum in range?
  800.         Int3(); // See rob
  801.         return 0;
  802.     }
  803.  
  804.     if (Objects[botnum].type != OBJ_ROBOT) { // Object is robot?
  805.         mprintf((1, "Non-robot explosion ignored.\n"));
  806. //      Int3(); // See rob
  807.         return 0;
  808.     }
  809.  
  810.     if (Objects[botnum].flags & OF_EXPLODING) { // Object not already exploding
  811.         return 0;
  812.     }
  813.  
  814.     // Data seems valid, explode the sucker
  815.  
  816.     if (Network_send_objects && network_objnum_is_past(botnum))
  817.     {
  818.         mprintf((0, "Resetting object sync due to object removal.\n"));
  819.         Network_send_objnum = -1;
  820.     }
  821.  
  822.     robot = &Objects[botnum];
  823.  
  824.     mprintf((0, "Robot %d controlled by player %d exploded.\n", botnum, robot->ctype.ai_info.REMOTE_OWNER));
  825.  
  826.     // Drop non-random KEY powerups locally only!
  827.     if ((robot->contains_count > 0) && (robot->contains_type == OBJ_POWERUP) && (Game_mode & GM_MULTI_COOP) && (robot->contains_id >= POW_KEY_BLUE) && (robot->contains_id <= POW_KEY_GOLD))
  828.     {
  829.         object_create_egg(robot);
  830.     }
  831.     else if (robot->ctype.ai_info.REMOTE_OWNER == Player_num) 
  832.     {
  833.         multi_drop_robot_powerups(robot-Objects);
  834.         multi_delete_controlled_robot(robot-Objects);
  835.     }
  836.  
  837.     if (Robot_info[robot->id].boss_flag) {
  838.         if (!Boss_dying)
  839.             start_boss_death_sequence(robot);
  840.         else
  841.             return 0;
  842.     } else
  843.         explode_object(robot, STANDARD_EXPL_DELAY);
  844.     return 1;
  845. }
  846.  
  847. void
  848. multi_do_robot_explode(char *buf)
  849. {
  850.     // Explode robot controlled by other player
  851.  
  852.     int botnum;
  853.     int loc = 1;
  854.     short killer;
  855.     int pnum;
  856.     int rval;
  857.  
  858.     pnum = buf[loc];                    loc += 1;
  859.     killer = objnum_remote_to_local(swapshort(*(short *)(buf+loc)), (byte)buf[loc+2]);
  860.                                             loc += 3;
  861.     botnum = objnum_remote_to_local(swapshort(*(short *)(buf+loc)), (byte)buf[loc+2]);
  862.                                             loc += 3;
  863.  
  864. //  Assert((botnum > -1) && (botnum <= Highest_object_index));
  865.  
  866.     if ((botnum < 0) || (botnum > Highest_object_index)) {
  867. //      Int3();
  868. //      mprintf((0, "Ignored explode message for robot I don't have.\n"));
  869.         return;
  870.     }
  871.  
  872.     rval = multi_explode_robot_sub(botnum, killer);
  873.  
  874.     if (rval && (killer == Players[Player_num].objnum))
  875.         add_points_to_score(Robot_info[Objects[botnum].id].score_value);
  876. }
  877.  
  878. extern fix EnergyToCreateOneRobot; // From fuelcen.c 
  879. extern object *create_morph_robot(segment *segp, vms_vector *object_pos, int object_id); // from fuelcen.c
  880.  
  881. void
  882. multi_do_create_robot(char *buf)
  883. {
  884.     
  885.     int fuelcen_num = buf[2];
  886.     int pnum = buf[1];
  887.     int type = buf[5];
  888.  
  889.     FuelCenter *robotcen;
  890.     vms_vector cur_object_loc, direction;
  891.     object *obj;
  892.     short objnum = swapshort(*(short *)(buf+3));
  893.  
  894.     if ((pnum < 0) || (objnum < 0) || (fuelcen_num < 0) || (fuelcen_num >= Num_fuelcenters) || (pnum >= N_players))
  895.     {
  896.         Int3(); // Bogus data
  897.         return;
  898.     }
  899.  
  900.     robotcen = &Station[fuelcen_num];
  901.  
  902.     mprintf((1, "NETWORK: Creating morph robot from matcen %d.\n", fuelcen_num)); // DEBUG
  903.  
  904.     // Play effect and sound
  905.  
  906.     compute_segment_center(&cur_object_loc, &Segments[robotcen->segnum]);
  907.     obj = object_create_explosion(robotcen->segnum, &cur_object_loc, i2f(10), VCLIP_MORPHING_ROBOT);
  908.     if (obj)
  909.         extract_orient_from_segment(&obj->orient, &Segments[robotcen->segnum]);
  910.     if (Vclip[VCLIP_MORPHING_ROBOT].sound_num > -1)
  911.         digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, robotcen->segnum, 0, &cur_object_loc, 0, F1_0 );
  912.  
  913.     // Set robot center flags, in case we become the master for the next one
  914.  
  915.     robotcen->Flag = 0;
  916.     robotcen->Capacity -= EnergyToCreateOneRobot;
  917.     robotcen->Timer = 0;
  918.  
  919.     obj = create_morph_robot(&Segments[robotcen->segnum], &cur_object_loc, type);
  920.     if (obj == NULL)
  921.         return; // Cannot create object!
  922.     
  923.     obj->matcen_creator = robotcen-Station | 0x80;
  924. //  extract_orient_from_segment(&obj->orient, &Segments[robotcen->segnum]);
  925.     vm_vec_sub( &direction, &ConsoleObject->pos, &obj->pos );
  926.     vm_vector_2_matrix( &obj->orient, &direction, &obj->orient.uvec, NULL);
  927.     morph_start( obj );
  928.  
  929.     mprintf((1, "matcen created robot %d (remote %d)\n", obj-Objects, objnum));
  930.     map_objnum_local_to_remote(obj-Objects, objnum, pnum);
  931.  
  932.     Assert(obj->ctype.ai_info.REMOTE_OWNER == -1);
  933. }
  934.  
  935. void
  936. multi_do_boss_actions(char *buf)
  937. {
  938.     // Code to handle remote-controlled boss actions
  939.  
  940.     object *boss_obj;
  941.     int boss_objnum;
  942.     int pnum;
  943.     int action, secondary;
  944.     int loc = 1;
  945.     short remote_objnum, segnum;
  946.  
  947.     pnum = buf[loc];                            loc += 1;
  948.     boss_objnum = swapshort(*(short *)(buf+loc));loc += 2;
  949.     action = buf[loc];                      loc += 1;
  950.     secondary = buf[loc];                   loc += 1;
  951.     remote_objnum = swapshort(*(short *)(buf+loc));loc += 2;
  952.     segnum = swapshort(*(short *)(buf+loc));           loc += 2;
  953.     
  954.     if ((boss_objnum < 0) || (boss_objnum > Highest_object_index))
  955.     {
  956.         Int3();  // See Rob
  957.         return;
  958.     }
  959.  
  960.     boss_obj = &Objects[boss_objnum];
  961.  
  962.     if ((boss_obj->type != OBJ_ROBOT) || !(Robot_info[boss_obj->id].boss_flag))
  963.     {
  964.         Int3(); // Got boss actions for a robot who's not a boss?
  965.         return;
  966.     }
  967.         
  968.     mprintf((0, "REMOTE: performing boss action %d.\n", action));
  969.  
  970.     switch(action) 
  971.     {
  972.         case 1: // Teleport
  973.             {   
  974.                 int teleport_segnum;
  975.                 vms_vector boss_dir;
  976.  
  977.                 if ((secondary < 0) || (secondary > Num_boss_teleport_segs))
  978.                 {
  979.                     Int3(); // Bad segnum for boss teleport, ROB!!
  980.                     return;
  981.                 }
  982.                 teleport_segnum = Boss_teleport_segs[secondary];
  983.                 mprintf((0, "Boss is teleporting, remove from other's list.\n"));
  984.                 if ((teleport_segnum < 0) || (teleport_segnum > Highest_segment_index))
  985.                 {
  986.                     Int3();  // See Rob
  987.                     return;
  988.                 }
  989.                 compute_segment_center(&boss_obj->pos, &Segments[teleport_segnum]);
  990.                 obj_relink(boss_obj-Objects, teleport_segnum);
  991.                 Last_teleport_time = GameTime;
  992.         
  993.                 vm_vec_sub(&boss_dir, &Objects[Players[pnum].objnum].pos, &boss_obj->pos);
  994.                 vm_vector_2_matrix(&boss_obj->orient, &boss_dir, NULL, NULL);
  995.  
  996.                 digi_link_sound_to_pos( Vclip[VCLIP_MORPHING_ROBOT].sound_num, teleport_segnum, 0, &boss_obj->pos, 0 , F1_0);
  997.                 digi_kill_sound_linked_to_object( boss_obj-Objects);
  998.                 digi_link_sound_to_object2( SOUND_BOSS_SHARE_SEE, boss_obj-Objects, 1, F1_0, F1_0*512 );    //  F1_0*512 means play twice as loud
  999.                 Ai_local_info[boss_obj-Objects].next_fire = 0;
  1000.  
  1001.                 if (boss_obj->ctype.ai_info.REMOTE_OWNER == Player_num)
  1002.                 {
  1003.                     mprintf((1, "WARNING: Accepted teleport message for boss when I controlled him!\n"));
  1004.                     multi_delete_controlled_robot(boss_objnum);
  1005. //                  robot_controlled[boss_obj->ctype.ai_info.REMOTE_SLOT_NUM] = -1;
  1006.                 }
  1007.  
  1008.                 boss_obj->ctype.ai_info.REMOTE_OWNER = -1; // Boss is up for grabs again!
  1009.                 boss_obj->ctype.ai_info.REMOTE_SLOT_NUM = 0; // Available immediately!
  1010.             }
  1011.             break;
  1012.         case 2: // Cloak
  1013.             Boss_hit_this_frame = 0;
  1014.             Boss_cloak_start_time = GameTime;
  1015.             Boss_cloak_end_time = GameTime + Boss_cloak_duration;
  1016.             boss_obj->ctype.ai_info.CLOAKED = 1;
  1017.             break;
  1018.         case 3: // Gate in robots!
  1019.             {
  1020.                 // Do some validity checking
  1021.                 if ( (remote_objnum >= MAX_OBJECTS) || (remote_objnum < 0) || (segnum < 0) || (segnum > Highest_segment_index) )
  1022.                 {
  1023.                     Int3(); // See Rob, bad data in boss gate action message
  1024.                     return;
  1025.                 }
  1026.  
  1027.                 // Gate one in!
  1028.                 if (gate_in_robot(secondary, segnum))
  1029.                     map_objnum_local_to_remote(Net_create_objnums[0], remote_objnum, pnum);
  1030.                 mprintf((0, "REMOTE: Boss gating in robot id %d, objnum %d (%d).\n", secondary, Net_create_objnums[0], remote_objnum));
  1031.             }
  1032.             break;
  1033.         case 4: // Start effect
  1034.             restart_effect(BOSS_ECLIP_NUM);
  1035.             break;
  1036.         case 5: // Stop effect
  1037.             stop_effect(BOSS_ECLIP_NUM);
  1038.             break;
  1039.         default:
  1040.             Int3(); // Illegal type to boss actions
  1041.     }
  1042. }
  1043.  
  1044. void
  1045. multi_do_create_robot_powerups(char *buf)
  1046. {
  1047.     // Code to drop remote-controlled robot powerups
  1048.  
  1049.     int loc = 1;
  1050.     object del_obj;
  1051.     int pnum, egg_objnum, i;
  1052.  
  1053.     pnum = buf[loc];                                    loc += 1;
  1054.     del_obj.contains_count = buf[loc];                  loc += 1;
  1055.     del_obj.contains_type = buf[loc];                   loc += 1;
  1056.     del_obj.contains_id = buf[loc];                     loc += 1;
  1057.     del_obj.segnum = swapshort(*(short *)(buf+loc));    loc += 2;
  1058.     del_obj.pos = *(vms_vector *)(buf+loc);             loc += 12;
  1059.     vm_vec_zero(&del_obj.mtype.phys_info.velocity);
  1060.  
  1061.     del_obj.pos.x = (fix)swapint((int)del_obj.pos.x);
  1062.     del_obj.pos.y = (fix)swapint((int)del_obj.pos.y);
  1063.     del_obj.pos.z = (fix)swapint((int)del_obj.pos.z);
  1064.  
  1065.     Assert((pnum >= 0) && (pnum < N_players));
  1066.  
  1067.     Net_create_loc = 0;
  1068.     srand(1245L);
  1069.  
  1070.     egg_objnum = object_create_egg(&del_obj);
  1071.  
  1072.     if (egg_objnum == -1)
  1073.         return; // Object buffer full
  1074.  
  1075. //  Assert(egg_objnum > -1);
  1076.     Assert((Net_create_loc > 0) && (Net_create_loc <= MAX_ROBOT_POWERUPS));
  1077.  
  1078.     for (i = 0; i < Net_create_loc; i++)
  1079.     {
  1080.         short s;
  1081.  
  1082.         s  = swapshort(*(short *)(buf+loc));
  1083.         if (s != -1)
  1084.             map_objnum_local_to_remote((short)Net_create_objnums[i], s, pnum);
  1085.         else
  1086.             Objects[Net_create_objnums[i]].flags |= OF_SHOULD_BE_DEAD; // Delete objects other guy didn't create one of
  1087.         loc += 2;
  1088.     }
  1089. }
  1090.  
  1091. void
  1092. multi_drop_robot_powerups(int objnum)
  1093. {
  1094.     // Code to handle dropped robot powerups in network mode ONLY!
  1095.  
  1096.     object *del_obj;
  1097.     int egg_objnum = -1;
  1098.     robot_info  *robptr; 
  1099.  
  1100.     if ((objnum < 0) || (objnum > Highest_object_index))
  1101.     {
  1102.         Int3();  // See rob
  1103.         return;
  1104.     }
  1105.     
  1106.     del_obj = &Objects[objnum];
  1107.  
  1108.     if (del_obj->type != OBJ_ROBOT)
  1109.     {
  1110.         Int3(); // dropping powerups for non-robot, Rob's fault
  1111.         return;
  1112.     }
  1113.  
  1114.     robptr = &Robot_info[del_obj->id];
  1115.  
  1116.     Net_create_loc = 0;
  1117.  
  1118.     if (del_obj->contains_count > 0) { 
  1119.         //  If dropping a weapon that the player has, drop energy instead, unless it's vulcan, in which case drop vulcan ammo.
  1120.         if (del_obj->contains_type == OBJ_POWERUP) {
  1121.             maybe_replace_powerup_with_energy(del_obj);
  1122.  
  1123.             // No key drops in non-coop games!
  1124.             if (!(Game_mode & GM_MULTI_COOP)) {
  1125.                 if ((del_obj->contains_id >= POW_KEY_BLUE) && (del_obj->contains_id <= POW_KEY_GOLD))
  1126.                     del_obj->contains_count = 0;
  1127.             }
  1128.         }
  1129.         srand(1245L);
  1130.         if (del_obj->contains_count > 0)
  1131.             egg_objnum = object_create_egg(del_obj);
  1132.     }
  1133.         
  1134.     else if (del_obj->ctype.ai_info.REMOTE_OWNER == -1) // No random goodies for robots we weren't in control of
  1135.         return;
  1136.  
  1137.     else if (robptr->contains_count) {
  1138.         srand(timer_get_approx_seconds());
  1139.         if (((rand() * 16) >> 15) < robptr->contains_prob) {
  1140.             del_obj->contains_count = ((rand() * robptr->contains_count) >> 15) + 1;
  1141.             del_obj->contains_type = robptr->contains_type;
  1142.             del_obj->contains_id = robptr->contains_id;
  1143.             if (del_obj->contains_type == OBJ_POWERUP)
  1144.                 maybe_replace_powerup_with_energy(del_obj);
  1145.             srand(1245L);
  1146.             if (del_obj->contains_count > 0)
  1147.                 egg_objnum = object_create_egg(del_obj);
  1148.         }
  1149.     }
  1150.  
  1151.     if (egg_objnum >= 0) {
  1152.         // Transmit the object creation to the other players        
  1153.         mprintf((0, "Dropped %d powerups for robot %d.\n", Net_create_loc, del_obj-Objects));
  1154.         multi_send_create_robot_powerups(del_obj);
  1155.     }
  1156. }
  1157.  
  1158. //  -----------------------------------------------------------------------------
  1159. //  Robot *robot got whacked by player player_num and requests permission to do something about it.
  1160. //  Note: This function will be called regardless of whether Game_mode is a multiplayer mode, so it
  1161. //  should quick-out if not in a multiplayer mode.  On the other hand, it only gets called when a
  1162. //  player or player weapon whacks a robot, so it happens rarely.
  1163. void multi_robot_request_change(struct object *robot, int player_num) {
  1164.     int slot, remote_objnum;
  1165.     byte dummy;
  1166.  
  1167.     if (!(Game_mode & GM_MULTI_ROBOTS))
  1168.         return;
  1169.     
  1170.     if (robot->ctype.ai_info.REMOTE_OWNER != Player_num)
  1171.         return;
  1172.  
  1173.     slot = robot->ctype.ai_info.REMOTE_SLOT_NUM;
  1174.  
  1175.     if ((slot < 0) || (slot >= MAX_ROBOTS_CONTROLLED)) {
  1176.         Int3();
  1177.         return;
  1178.     }
  1179.  
  1180.     remote_objnum = objnum_local_to_remote(robot-Objects, &dummy);
  1181.     if (remote_objnum < 0)
  1182.         return;
  1183.  
  1184.     mprintf((0, "request_change(): my pri %d, player %d's pri %d.\n", MULTI_ROBOT_PRIORITY(remote_objnum, Player_num),
  1185.               player_num, MULTI_ROBOT_PRIORITY(remote_objnum, player_num)));
  1186.  
  1187.     if ( (robot_agitation[slot] < 70) || (MULTI_ROBOT_PRIORITY(remote_objnum, player_num) > MULTI_ROBOT_PRIORITY(remote_objnum, Player_num)) || (rand() > 0x4400))
  1188.     {
  1189.         mprintf((0, "Robot %d (%d) released because it got hit by Player %d.\n", robot-Objects, remote_objnum, player_num));
  1190.         if (robot_send_pending[slot])
  1191.             multi_send_robot_position(robot_controlled[slot], -1);
  1192.         multi_send_release_robot(robot_controlled[slot]);
  1193.         robot->ctype.ai_info.REMOTE_SLOT_NUM = 5;  // Hands-off period
  1194.     }
  1195. }
  1196.  
  1197. #endif
  1198.